#include "list.h"
#include <stdlib.h>
#include <stdio.h>

/**
 * Initialise une liste vide
 */
void list_initialize(List* list)
{
    list->first = NULL;
    list->last = NULL;
    list->count = 0;
}

/**
 * Initialise une copie de la liste src dans la liste dest 
 */
void list_initializeCopy(List* list, const List* source)
{
    list->first = source->first;
    list->last = source->last;
    list->count = source->count;
}

/**
 * Désaloue l'ensemble des ressources de la liste passée en argument
 */
void list_destroy(List** list)
{
    list_clear(*list);
    free(*list);
    *list = NULL;
}

/**
 * Copie en mémoire la liste src dans la list dest passé en argument
 */
void list_copy(List* list, const List* source)
{
    Cell* cell = list_getFirst(source);
    Cell* tmp = NULL;
    while (cell != NULL)
    {
        cell_create(&tmp);
    }
    list->first = source->first;
    list->last = source->last;
    list->count = source->count;
}

/**
 * Supprimme l'ensemble des cellule contenues dans la liste passée en argument
 */
void list_clear(List* list)
{
    Cell* cell = list_getFirst(list);
    while (cell != NULL)
    {
        list->first = cell->next;
        cell_destroy(&(cell));
        cell = list->first;
    }
    list->first = NULL;
    list->last = NULL;
    list->count = 0;
}

/**
 * Obtient le nombre de cellules contenue dans la liste passée en argument
 */
int list_getCount(const List* list)
{
    return list->count;
}

/**
 * Obtient l'adresse de la première cellule de la liste passée en argument
 */
Cell* list_getFirst(const List* list)
{
    return list->first;
}

/**
 * Obtient la valeur de la première cellule de la liste passée en argument
 */
void* list_getFirstValue(const List* list)
{
    return list_getFirst(list)->value;
}

/**
 * Obtient l'adresse de la dernière cellule de la liste passée en argument
 */
Cell* list_getLast(const List* list)
{
    return list->last;
}

/**
 * Obtient la valeur de la dernière cellule de la liste passée en argument
 */
void* list_getLastValue(const List* list)
{
    return list_getLast(list)->value;
}

/**
 * Obtient l'adresse de la cellule dans la liste à l'index passé en argument  
 * @param list L'adresse de la liste
 * @return L'adresse de la cellule à l'index passée en argument ou NULL si non trouvé
 */
Cell* list_getAt(const List* list, int index)
{
    if(list->count > index)
    {
        int i;
        Cell* cell = list_getFirst(list);
        for(i = 0; i < index; i++)
            cell = cell->next;
        return cell;
    }
    else
        return NULL;
}

/**
 * Obtient la valeur de la cellule dans la liste à l'index passé en argument
 * @param list L'adresse de la liste
 * @return L'adresse de la cellule à l'index passée en argument ou NULL si non trouvé
 */
void* list_getValueAt(const List* list, int index)
{
    return list_getAt(list,index)->value;
}

/**
 * Supprime la cellule dans la liuste à l'index passé en argument
 * @param list L'adresse de la liste
 * @return 0 si l'index ne fait pas parti de la liste, 1 sinon
 */
int list_removeAt(List* list, int index)
{
    if(list->count > index)
    {
        int i;
        Cell *previous = NULL,  *cell = list_getFirst(list);
        for(i = 0; i < index; i++)
        {
            previous = cell;
            cell = cell->next;
        }
        if(list->last == cell)
            list->last = previous;
        if(previous == NULL)
            list->first = cell->next;
        else
            previous->next = cell->next;
        cell_destroy(&cell);
        list->count--;
        return i;
    }
    else
        return -1;
}

/**
 * Obtient l'adresse de la prémière cellule de la liste dont l'élément est égale à celui passé en argument
 * @param list L'adresse de la liste
 * @param cell Resultat : l'adresse de la cellule trouvée, ou NULL si aucun élément correspondant n'a été trouvé
 * @return l'index de la cellule trouvée, ou -1 si aucun élément correspondant n'a été trouvé
 */
int list_get(const List* list, void* value, Cell** cell)
{
    int i = 0;
    (*cell) = list_getFirst(list);
    while ((*cell) != NULL)
    {
        (*cell) = cell_getNext((*cell));
        if((*cell)->value == value)
            return i;
        i++;
    }
    return -1;
}

int list_getFirstOf(const List* list, void* value, int (*pIsEqual)(void*,void*), Cell** cell)
{
    int i = 0;
    (*cell) = list_getFirst(list);
    while ((*cell) != NULL)
    {
        (*cell) = cell_getNext((*cell));
        if(pIsEqual((*cell)->value,value))
            return i;
        i++;
    }
    return -1;
}


/**
 * Supprime la cellule de la liste dont l'élément est égale à celui passé en argument
 * @return 1 si la cellule a été supprimée, 0 si aucun élément correspondant n'a été trouvé
 */
int list_remove(List* list, void* value)
{
    int i = 0;
    Cell *previous = NULL, *cell = list_getFirst(list);
    while(cell != NULL)
    {
        if(cell->value == value)
        {
            if(list->last == cell)
                list->last = previous;
            if(previous == NULL)
                list->first = cell->next;
            else
                previous->next = cell->next;
            cell_destroy(&cell);
            list->count--;
            return i;
        }
        previous = cell;
        cell = cell->next;
        i++;
    }
    return 0;
}

/**
 * Supprime la cellule de la liste dont l'élément est égale à celui passé en argument
 * @return 1 si la cellule a été supprimée, 0 si aucun élément correspondant n'a été trouvé
 */
int list_removeFirstOf(List* list, void* value, int (*pIsEqual)(void*,void*))
{
    int i = 0;
    Cell *previous = NULL, *cell = list_getFirst(list);
    while(cell != NULL)
    {
        if(pIsEqual(cell->value,value))
        {
            if(list->last == cell)
                list->last = previous;
            if(previous == NULL)
                list->first = cell->next;
            else
                previous->next = cell->next;
            cell_destroy(&cell);
            list->count--;
            return i;
        }
        previous = cell;
        cell = cell->next;
        i++;
    }
    return 0;
}

/**
 * Insère l'élément dans la liste à l'index passée en argument
 * @return
 */
void list_insertAt(List* list, void* value, int index)
{
    Cell *previous = list_getAt(list,index), *cell;
    cell_create(&cell);
    cell->value = value;
    if(list->count < index) /*Si l'index est supérieure au nombre d'élément on le normalise au nombre d'éléments*/
    {
        index = list->count;
    }
    if(list->count > 0)
    {
        if(index == list->count)
        {
            cell->next = NULL;
            list->last->next = cell;
            list->last = cell;
        }
        else if(index == 0)
        {
            cell->next = list->first;
            list->first = cell;
        }
        else
        {
            cell->next = previous->next;
            previous->next = cell;
        }
    }
    else
    {
        cell->next = NULL;
        list->first = cell;
        list->last = cell;
    }
    list->count++;
}

/**
  * Insère l'élément en première position de la liste passée en argument
  */
void list_insertFirst(List* list, void* value)
{
    list_insertAt(list,value,0);
}

/**
 * Insère l'élément en dernière position de la liste passée en argument
 */
void list_insertLast(List* list, void* value)
{
    list_insertAt(list,value,list_getCount(list));
}

